import json
import re
import time
import threading
from datetime import datetime, timedelta
from typing import List, Dict, Any, Optional
import requests
import pymysql
from bridge.context import ContextType
from bridge.reply import Reply, ReplyType
from common.log import logger
import plugins
from plugins import Plugin
from plugins.event import Event, EventAction
from common import const


@plugins.register(name="WechatTimer", desc="企业微信定时消息插件", version="1.0", author="Assistant", desire_priority=999)
class WechatTimer(Plugin):
    def __init__(self):
        super().__init__()
        
        # 初始化处理器
        self.handlers = {}
        
        # 企业微信配置
        self.corp_id = "ww296cfae07117646a"
        self.corp_secret = "zAXsRmmCZLpaF8QHUOBGE6N6_8y9tHft1CyHYrCtVIM"
        self.agent_id = "1000071"
        self.access_token = None
        self.token_expire_time = 0
        
        # 数据库配置
        self.db_config = {
            'host': 'rm-t4nq3pd7v1b9k839kao.mysql.singapore.rds.aliyuncs.com',
            'port': 3306,
            'user': 'whisper',
            'password': 'Fp888999',
            'database': 'wecom_doc_week',
            'charset': 'utf8mb4'
        }
        
        # 大模型API配置
        try:
            config = self.load_config()
            llm_config = config.get('wechat_timer', {}).get('llm_config', {})
            self.llm_api_key = llm_config.get('api_key', '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')
            self.llm_api_url = llm_config.get('api_url', 'https://api.openai.com/v1/chat/completions')
            self.llm_model = llm_config.get('model', 'gpt-4.1-mini')
        except Exception as e:
            logger.error(f"[WechatTimer] 加载大模型API配置失败: {e}")
            self.llm_api_key = "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"
            self.llm_api_url = "https://api.openai.com/v1/chat/completions"
            self.llm_model = "gpt-4.1"
        
        # 存储定时任务的内存列表
        self.timer_tasks = []
        
        # 定时待办任务提取配置
        self.todo_groups = []  # 需要定时提取待办任务的群组列表
        
        # 启动定时任务调度器
        self.scheduler_thread = threading.Thread(target=self._run_scheduler, daemon=True)
        self.scheduler_thread.start()
        
        logger.info("[WechatTimer] 企业微信定时消息插件初始化完成")
        
        # 注册事件处理器
        self.handlers[Event.ON_HANDLE_CONTEXT] = self.on_handle_context

    def on_handle_context(self, e_context):
        if e_context['context'].type != ContextType.TEXT:
            return

        content = e_context['context']['content'].strip()
        logger.info(f"[WechatTimer] 收到消息: {content}")
        
        # 检查是否为定时消息指令
        if self._is_timer_command(content):
            # 原有的定时消息指令处理逻辑
            try:
                # 解析指令参数
                params = self._parse_command(content)
                if not params:
                    reply = Reply(ReplyType.TEXT, "指令格式错误，请使用:\n1. #定时消息 #用户ID #时间 #消息内容\n2. #定时待办 #群ID #用户ID")
                    e_context['reply'] = reply
                    e_context.action = EventAction.BREAK_PASS
                    return
                
                # 保存定时任务到内存
                task_id = self._save_timer_task(params)
                
                if task_id:
                    # 根据任务类型生成不同的描述
                    if params['type'] == 'message':
                        description = f"定时消息任务已创建，任务ID: {task_id}\n将在 {params['send_time']} 发送给 {params['user_id']}"
                    elif params['type'] == 'todo':
                        description = f"定时待办任务已创建，任务ID: {task_id}\n将在 {params['send_time']} 提取 {params['group_id']} 群的待办任务并发送给 {params['user_id']}"
                    
                    reply = Reply(ReplyType.TEXT, description)
                else:
                    reply = Reply(ReplyType.TEXT, "创建定时任务失败，请检查配置")
                
                e_context['reply'] = reply
                e_context.action = EventAction.BREAK_PASS
                
            except Exception as e:
                logger.error(f"[WechatTimer] 处理定时消息指令时出错: {str(e)}")
                reply = Reply(ReplyType.TEXT, f"处理指令时出错: {str(e)}")
                e_context['reply'] = reply
                e_context.action = EventAction.BREAK_PASS
        
        else:
            # 检查是否有正在等待修改时间的任务
            waiting_time_change_tasks = [
                task for task in self.timer_tasks 
                if task.get('status') == 'waiting_time_change'
            ]
            
            if waiting_time_change_tasks:
                # 如果有等待修改时间的任务，尝试解析输入的时间
                task = waiting_time_change_tasks[0]
                new_send_time = self._parse_time(content)
                
                if new_send_time:
                    # 更新任务时间
                    task['specific_send_time'] = new_send_time
                    task['status'] = 'pending'
                    
                    # 获取之前保存的响应码
                    response_code = task.pop('waiting_time_change_response_code', None)
                    
                    # 更新卡片消息
                    self._send_replacement_card(task, f"提醒时间已修改为 {content}", response_code)
                    
                    # 回复用户
                    reply = Reply(ReplyType.TEXT, f"任务提醒时间已成功修改为 {content}，将在 {new_send_time.strftime('%Y-%m-%d %H:%M:%S')} 推送")
                    e_context['reply'] = reply
                    e_context.action = EventAction.BREAK_PASS
                else:
                    # 时间解析失败，引导用户重新输入
                    reply = Reply(ReplyType.TEXT, "时间格式错误，请重新输入正确的时间。\n支持的格式如：明天9:00、2024-01-01 14:30、5分钟后")
                    e_context['reply'] = reply
                    e_context.action = EventAction.BREAK_PASS

    def _is_timer_command(self, content: str) -> bool:
        """检查是否为定时消息指令"""
        # 匹配格式: #定时消息 #用户ID #时间 #消息内容
        pattern1 = r'#定时消息\s+#([^#]+)\s+#([^#]+)\s+#(.+)'
        # 匹配格式: #定时待办 #群ID #用户ID
        pattern2 = r'#定时待办\s+#([^#]+)\s+#([^#]+)'
        return bool(re.match(pattern1, content) or re.match(pattern2, content))

    def _parse_command(self, content: str) -> Optional[Dict[str, Any]]:
        """解析指令参数"""
        try:
            # 尝试解析定时消息指令
            pattern1 = r'#定时消息\s+#([^#]+)\s+#([^#]+)\s+#(.+)'
            match1 = re.match(pattern1, content)
            
            if match1:
                user_id = match1.group(1).strip()
                time_str = match1.group(2).strip()
                message = match1.group(3).strip()
                
                # 解析时间格式
                send_time = self._parse_time(time_str)
                if not send_time:
                    return None
                
                return {
                    'type': 'message',
                    'user_id': user_id,
                    'send_time': send_time,
                    'message': message
                }
            
            # 尝试解析定时待办指令
            pattern2 = r'#定时待办\s+#([^#]+)\s+#([^#]+)'
            match2 = re.match(pattern2, content)
            
            if match2:
                group_id = match2.group(1).strip()
                user_id = match2.group(2).strip()
                
                # 默认设置为每天9:00执行
                send_time = self._parse_time("每天9:00")
                if not send_time:
                    return None
                
                return {
                    'type': 'todo',
                    'group_id': group_id,
                    'user_id': user_id,
                    'send_time': send_time
                }
            
            return None
        except Exception as e:
            logger.error(f"[WechatTimer] 解析指令参数失败: {str(e)}")
            return None

    def _parse_time(self, time_str: str) -> Optional[datetime]:
        """解析时间字符串"""
        try:
            # 支持多种时间格式
            formats = [
                '%Y-%m-%d %H:%M:%S',
                '%Y-%m-%d %H:%M',
                '%m-%d %H:%M',
                '%H:%M'
            ]
            
            for fmt in formats:
                try:
                    if fmt == '%H:%M':
                        # 只有时分，使用今天的日期
                        today = datetime.now().date()
                        time_part = datetime.strptime(time_str, fmt).time()
                        return datetime.combine(today, time_part)
                    else:
                        return datetime.strptime(time_str, fmt)
                except ValueError:
                    continue
            
            # 支持相对时间，如"5分钟后"、"1小时后"
            if '分钟后' in time_str:
                minutes = int(time_str.replace('分钟后', ''))
                return datetime.now() + timedelta(minutes=minutes)
            elif '小时后' in time_str:
                hours = int(time_str.replace('小时后', ''))
                return datetime.now() + timedelta(hours=hours)
            elif '天后' in time_str:
                days = int(time_str.replace('天后', ''))
                return datetime.now() + timedelta(days=days)
            elif '每天' in time_str:
                # 处理每天格式，如"每天09:00"
                time_part = time_str.replace('每天', '').strip()
                try:
                    time_obj = datetime.strptime(time_part, '%H:%M').time()
                    # 计算今天的这个时间
                    today = datetime.now().date()
                    target_time = datetime.combine(today, time_obj)
                    # 如果今天这个时间已经过了，就设置为明天
                    if target_time <= datetime.now():
                        target_time += timedelta(days=1)
                    return target_time
                except ValueError:
                    return None
            elif '每周' in time_str:
                # 处理每周格式，如"每周一09:00"
                # 这里简化处理，设置为明天
                return datetime.now() + timedelta(days=1)
                
            return None
        except Exception as e:
            logger.error(f"[WechatTimer] 解析时间失败: {str(e)}")
            return None

    def _save_timer_task(self, params: Dict[str, Any]) -> Optional[str]:
        """保存定时任务到内存"""
        try:
            task_id = f"timer_{int(time.time())}"
            task = {
                'task_id': task_id,
                'type': params['type'],
                'status': 'pending',
                'created_at': datetime.now()
            }
            
            # 根据任务类型添加不同字段
            if params['type'] == 'message':
                task['user_id'] = params['user_id']
                task['message'] = params['message']
                task['send_time'] = params['send_time']
            elif params['type'] == 'todo':
                task['group_id'] = params['group_id']
                task['user_id'] = params['user_id']
                task['send_time'] = params['send_time']
                task['retry_count'] = 0  # 初始化重试次数
                task['max_retries'] = 3  # 最大重试次数
            
            self.timer_tasks.append(task)
            logger.info(f"[WechatTimer] 定时任务已保存到内存: {task_id}")
            return task_id
            
        except Exception as e:
            logger.error(f"[WechatTimer] 保存定时任务失败: {str(e)}")
            return None

    def _split_content(self, content: str, max_length: int = 512) -> List[str]:
        """
        将长内容分段显示，确保每段不超过指定长度
        
        Args:
            content: 原始内容
            max_length: 每段最大长度
        
        Returns:
            内容分段列表
        """
        # 如果内容本身不超长，直接返回
        if len(content) <= max_length:
            return [content]
        
        parts = []
        current_part = ""
        
        # 按行分割内容
        lines = content.split('\n')
        
        for line in lines:
            # 如果当前行本身超长，需要强制截断
            while len(line) > max_length:
                # 尽量在句号处截断
                period_index = line[:max_length].rfind('。')
                if period_index == -1:
                    # 如果没有句号，直接截取
                    parts.append(line[:max_length])
                    line = line[max_length:]
                else:
                    parts.append(line[:period_index + 1])
                    line = line[period_index + 1:]
            
            # 检查加入当前行后是否会超长
            if len(current_part) + len(line) + 1 > max_length:
                parts.append(current_part.strip())
                current_part = line
            else:
                # 添加换行符（除了第一行）
                current_part += '\n' + line if current_part else line
        
        # 添加最后一部分
        if current_part:
            parts.append(current_part.strip())
        
        # 确保每个部分不超过最大长度
        final_parts = []
        for part in parts:
            while len(part) > max_length:
                # 尽量在句号处截断
                period_index = part[:max_length].rfind('。')
                if period_index == -1:
                    # 如果没有句号，直接截取
                    final_parts.append(part[:max_length])
                    part = part[max_length:]
                else:
                    final_parts.append(part[:period_index + 1])
                    part = part[period_index + 1:]
            
            # 添加剩余部分
            if part:
                final_parts.append(part)
        
        return final_parts

    def _send_interactive_card_message(self, task: Dict[str, Any]) -> None:
        """发送交互式卡片消息"""
        try:
            # 获取访问令牌
            access_token = self._get_access_token()
            if not access_token:
                logger.error("[WechatTimer] 获取访问令牌失败")
                return

            # 读取卡片消息配置
            try:
                config = self.load_config()
                card_config = config.get('wechat_timer', {}).get('card_message_config', {})
            except Exception as e:
                logger.warning(f"[WechatTimer] 读取卡片消息配置失败，使用默认配置: {e}")
                card_config = {}

            # 根据任务类型处理消息内容和待办任务
            todos = []
            if task['type'] == 'message':
                message_content = task['message']
            elif task['type'] == 'todo':
                # 提取待办任务
                todos = self._extract_todos_from_history(task['group_id'], task['user_id'])
                
                # 如果没有待办任务，发送文本消息
                if not todos:
                    logger.info(f"[WechatTimer] 未发现待办任务，发送文本消息: {task}")
                    
                    # 从配置读取搜索天数
                    try:
                        config = self.load_config()
                        todo_extract_config = config.get('wechat_timer', {}).get('todo_extract_config', {})
                        days_to_search = todo_extract_config.get('days_to_search', 2)
                    except Exception as e:
                        days_to_search = 2
                    
                    # 发送普通文本消息
                    url = f"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={access_token}"
                    
                    text_message = {
                        "touser": task['user_id'],
                        "msgtype": "text",
                        "agentid": self.agent_id,
                        "text": {
                            "content": f"群聊 {task['group_id']} 最近 {days_to_search} 天未发现您的待办任务。"
                        }
                    }
                    
                    response = requests.post(url, json=text_message)
                    result = response.json()
                    
                    if result.get('errcode') == 0:
                        logger.info(f"[WechatTimer] 未发现待办任务，文本消息发送成功: {task['task_id']}")
                        task['status'] = 'sent'
                    else:
                        logger.error(f"[WechatTimer] 未发现待办任务，文本消息发送失败: {result}")
                        task['status'] = 'failed'
                    
                    return

            else:
                logger.error(f"[WechatTimer] 未知任务类型: {task['type']}")
                task['status'] = 'failed'
                return

            # 生成唯一的任务ID
            task_id = f"timer_{int(time.time())}_{hash(str(task))}"
            
            # 构造符合企业微信API的消息体
            message = {
                "touser": task['user_id'],  # 接收者的userid
                "msgtype": "template_card",  # 消息类型，使用模板卡片
                "agentid": self.agent_id,  # 应用ID
                "template_card": {
                    "card_type": "button_interaction",  # 卡片类型
                    "source": card_config.get('source', {
                        "icon_url": "https://wework.qpic.cn/wwpic/252813_jOfDHtcISzuodLa_1629280209/0",
                        "desc": "任务管理",
                        "desc_color": 0
                    }),
                    "main_title": {
                        "title": card_config.get('main_title', {}).get('title', "待办任务通知")
                    },
                    "task_id": task_id,  # 使用新生成的唯一任务ID
                    "button_selection": {
                        "question_key": "task_action",
                        "option_list": [
                            {
                                "id": "complete",
                                "text": "完成"
                            },
                            {
                                "id": "change_time",
                                "text": "改时间"
                            },
                            {
                                "id": "cancel",
                                "text": "不再提醒"
                            }
                        ]
                    },
                    "button_list": [
                        {
                            "text": "提交",
                            "style": 1,
                            "key": "submit_key"
                        }
                    ],
                    "replace_card": True,  # 提交后替换卡片
                    "horizontal_content_list": [
                        {
                            "keyname": f"任务{i+1}",
                            "value": f"{todo.get('task_title', '未知任务')}，截止时间: {todo.get('deadline', '未指定')}"
                        } for i, todo in enumerate(todos)  # 显示所有任务
                    ]
                }
            }

            # 发送消息
            url = f"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={access_token}"
            response = requests.post(url, json=message)
            result = response.json()
            
            if result.get('errcode') == 0:
                logger.info(f"[WechatTimer] 交互式卡片消息发送成功: {task_id}")
                # 更新任务状态和信息
                task['message_id'] = result.get('msgid')
                task['status'] = 'sent'
                task['send_time'] = datetime.now()
                task['task_id'] = task_id  # 更新任务ID
            else:
                logger.error(f"[WechatTimer] 交互式卡片消息发送失败: {result}")
                task['status'] = 'failed'

        except Exception as e:
            logger.error(f"[WechatTimer] 发送交互式卡片消息失败: {str(e)}", exc_info=True)
            task['status'] = 'failed'

    def handle_card_message_callback(self, callback_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        处理企业微信模板卡片消息回调
        
        Args:
            callback_data: 回调数据字典
        
        Returns:
            处理结果字典
        """
        try:
            # 打印详细回调日志
            logger.info(f"[WechatTimer] 收到卡片消息回调: {callback_data}")
            
            # 获取关键信息
            selected_option = callback_data.get('selected_option_id')
            task_id = callback_data.get('task_id')
            response_code = callback_data.get('response_code')
            
            # 查找对应的任务
            task = next((t for t in self.timer_tasks if t['task_id'] == task_id), None)
            
            if not task:
                logger.warning(f"[WechatTimer] 未找到对应的任务: {task_id}")
                return {"errcode": 40001, "errmsg": "Task not found"}
            
            # 处理不同选项
            if selected_option == 'complete':
                # 标记任务完成
                task['status'] = 'completed'
                self._send_replacement_card(task, "任务已完成", response_code)
            
            elif selected_option == 'change_time':
                # 标记任务为等待修改时间状态
                task['status'] = 'waiting_time_change'
                task['waiting_time_change_response_code'] = response_code
                
                # 发送文本消息询问具体时间
                try:
                    access_token = self._get_access_token()
                    if not access_token:
                        logger.error("[WechatTimer] 获取访问令牌失败")
                        return {"errcode": 40002, "errmsg": "Failed to get access token"}
                    
                    # 发送询问时间的消息
                    url = f"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={access_token}"
                    
                    message_data = {
                        "touser": task['user_id'],
                        "msgtype": "text",
                        "agentid": self.agent_id,
                        "text": {
                            "content": f"请输入新的提醒时间。\n支持的时间格式：\n- 明天9:00\n- 2024-01-01 14:30\n- 5分钟后\n\n请直接回复您希望的提醒时间。"
                        }
                    }
                    
                    response = requests.post(url, json=message_data)
                    result = response.json()
                    
                    if result.get('errcode') == 0:
                        logger.info(f"[WechatTimer] 发送修改时间询问消息成功: {task_id}")
                    else:
                        logger.error(f"[WechatTimer] 发送修改时间询问消息失败: {result}")
                
                except Exception as e:
                    logger.error(f"[WechatTimer] 发送修改时间询问消息异常: {str(e)}")
            
            elif selected_option == 'cancel':
                # 取消任务提醒
                task['status'] = 'cancelled'
                self._send_replacement_card(task, "任务已取消", response_code)
            
            return {"errcode": 0, "errmsg": "ok"}
        
        except Exception as e:
            logger.error(f"[WechatTimer] 处理卡片消息回调失败: {str(e)}", exc_info=True)
            return {"errcode": 50001, "errmsg": f"Internal error: {str(e)}"}

    def handle_input_form_callback(self, callback_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        处理输入框回调
        
        Args:
            callback_data: 回调数据字典
        
        Returns:
            处理结果字典
        """
        try:
            task_id = callback_data.get('task_id')
            input_values = callback_data.get('input_values', {})
            response_code = callback_data.get('response_code')
            
            task = next((t for t in self.timer_tasks if t['task_id'] == task_id), None)
            
            if not task:
                return {"errcode": 40001, "errmsg": "Task not found"}
            
            if 'later_time' in input_values:
                # 处理稍后提醒
                later_time = input_values['later_time']
                # 解析时间并设置下一次提醒时间
                next_remind_time = self._parse_time(later_time)
                if next_remind_time:
                    task['next_remind_time'] = next_remind_time
                    task['status'] = 'pending'
                    self._send_replacement_card(task, f"将在 {later_time} 后提醒", response_code)
                else:
                    logger.error(f"[WechatTimer] 解析稍后提醒时间失败: {later_time}")
                    return {"errcode": 40002, "errmsg": "Invalid time format"}
            
            elif 'new_remind_time' in input_values:
                # 处理修改提醒时间
                new_time = input_values['new_remind_time']
                # 解析并更新任务时间
                send_time = self._parse_time(new_time)
                if send_time:
                    task['send_time'] = send_time
                    task['status'] = 'pending'
                    self._send_replacement_card(task, f"提醒时间已修改为 {new_time}", response_code)
                else:
                    logger.error(f"[WechatTimer] 解析新提醒时间失败: {new_time}")
                    return {"errcode": 40002, "errmsg": "Invalid time format"}
            
            return {"errcode": 0, "errmsg": "ok"}
        
        except Exception as e:
            logger.error(f"[WechatTimer] 处理输入框回调失败: {str(e)}", exc_info=True)
            return {"errcode": 50001, "errmsg": f"Internal error: {str(e)}"}

    def handle_text_input_callback(self, callback_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        处理文本输入回调
        
        Args:
            callback_data: 回调数据字典
        
        Returns:
            处理结果字典
        """
        try:
            task_id = callback_data.get('task_id')
            text_input = callback_data.get('text_input', '').strip()
            
            # 查找对应的任务
            task = next((t for t in self.timer_tasks if t['task_id'] == task_id), None)
            
            if not task:
                logger.warning(f"[WechatTimer] 未找到对应的任务: {task_id}")
                return {"errcode": 40001, "errmsg": "Task not found"}
            
            # 检查任务是否处于等待修改时间状态
            if task.get('status') != 'waiting_time_change':
                logger.warning(f"[WechatTimer] 任务 {task_id} 不在等待修改时间状态")
                return {"errcode": 40002, "errmsg": "Invalid task status"}
            
            # 解析输入的时间
            new_send_time = self._parse_time(text_input)
            
            if not new_send_time:
                # 时间解析失败，引导用户重新输入
                return {
                    "errcode": 0,
                    "errmsg": "ok",
                    "text_form": {
                        "title": "时间格式错误",
                        "placeholder": "请重新输入正确的时间，例如：明天9:00、2024-01-01 14:30、5分钟后",
                        "tips": "支持多种时间格式，如：明天9:00、2024-01-01 14:30、5分钟后"
                    }
                }
            
            # 更新任务时间
            task['send_time'] = new_send_time
            task['status'] = 'pending'
            
            # 获取之前保存的响应码
            response_code = task.pop('waiting_time_change_response_code', None)
            
            # 更新卡片消息
            self._send_replacement_card(task, f"提醒时间已修改为 {text_input}", response_code)
            
            return {"errcode": 0, "errmsg": "ok"}
        
        except Exception as e:
            logger.error(f"[WechatTimer] 处理文本输入回调失败: {str(e)}", exc_info=True)
            return {"errcode": 50001, "errmsg": f"Internal error: {str(e)}"}

    def _run_scheduler(self):
        """运行定时任务调度器"""
        while True:
            try:
                # 检查待发送的定时任务
                self._check_pending_tasks()
                # 打印当前任务状态（调试用）
                self._log_tasks_status()
                
                # 每30秒检查一次
                time.sleep(30)
            except Exception as e:
                logger.error(f"[WechatTimer] 调度器运行出错: {str(e)}")
                time.sleep(60)

    def _check_pending_tasks(self):
        """检查待发送的定时任务"""
        try:
            now = datetime.now()
            tasks_to_send = []
            
            # 找出到期的任务
            for task in self.timer_tasks:
                # 检查原有的定时任务
                if task['status'] == 'pending' and task['send_time'] <= now:
                    tasks_to_send.append(task)
                
                # 检查特定时间的任务（如修改时间后的任务）
                if task.get('specific_send_time') and task['specific_send_time'] <= now:
                    # 重新发送任务卡片
                    tasks_to_send.append(task)
                    # 清除特定发送时间
                    task.pop('specific_send_time', None)
            
            # 发送到期的任务
            for task in tasks_to_send:
                # 如果是特定时间的任务，重新设置为原始任务
                if 'specific_send_time' in task:
                    task['send_time'] = task['specific_send_time']
                    task.pop('specific_send_time', None)
                
                # 重新发送交互式卡片消息
                self._send_interactive_card_message(task)
                
        except Exception as e:
            logger.error(f"[WechatTimer] 检查定时任务失败: {str(e)}")

    def _check_retry_tasks(self) -> None:
        """
        检查需要重试的任务
        已删除所有拒绝和重试相关的逻辑
        """
        try:
            # 保持方法结构，但不执行任何操作
            pass
        except Exception as e:
            logger.error(f"[WechatTimer] 检查重试任务失败: {str(e)}")

    def _extract_todos_from_history(self, group_id: str, target_user: str) -> List[Dict[str, Any]]:
        """从历史聊天记录中提取待办任务列表
        
        Args:
            group_id: 群组ID
            target_user: 目标用户名
        
        Returns:
            待办任务列表
        """
        try:
            # 从配置读取搜索天数
            try:
                config = self.load_config()
                todo_extract_config = config.get('wechat_timer', {}).get('todo_extract_config', {})
                days_to_search = todo_extract_config.get('days_to_search', 2)
            except Exception as e:
                logger.warning(f"[WechatTimer] 读取待办任务提取配置失败，使用默认值: {e}")
                days_to_search = 2
            
            # 计算日期范围
            now = datetime.now()
            search_start_date = now - timedelta(days=days_to_search)
            start_date = search_start_date.replace(hour=0, minute=0, second=0, microsecond=0)
            end_date = now.replace(hour=23, minute=59, second=59, microsecond=999999)
            
            # 获取指定日期范围的聊天记录
            chat_records = self._get_chat_records(group_id, None, start_date, end_date)
            
            if not chat_records:
                logger.info(f"[WechatTimer] 群聊 '{group_id}' 最近 {days_to_search} 天没有聊天记录")
                return []
            
            # 使用大模型提取指定用户的待办任务
            todos = self._extract_todos_with_llm(chat_records, target_user)
            
            if not todos:
                logger.info(f"[WechatTimer] 群聊 '{group_id}' 最近 {days_to_search} 天未发现 {target_user} 的待办任务")
                return []
            
            return todos
            
        except Exception as e:
            logger.error(f"[WechatTimer] 提取待办任务失败: {str(e)}")
            return []

    def _extract_yesterday_todos(self, group_id: str, target_user: str) -> str:
        """提取历史待办任务（返回格式化的消息字符串）
        
        Args:
            group_id: 群组ID
            target_user: 目标用户名
        
        Returns:
            格式化的待办任务消息
        """
        try:
            # 提取待办任务列表
            todos = self._extract_todos_from_history(group_id, target_user)
            
            # 从配置读取搜索天数
            try:
                config = self.load_config()
                todo_extract_config = config.get('wechat_timer', {}).get('todo_extract_config', {})
                days_to_search = todo_extract_config.get('days_to_search', 2)
            except Exception as e:
                days_to_search = 2
            
            if not todos:
                return f"群聊 '{group_id}' 最近 {days_to_search} 天未发现 {target_user} 的待办任务"
            
            # 计算日期范围
            now = datetime.now()
            search_start_date = now - timedelta(days=days_to_search)
            
            # 格式化待办任务
            return self._format_todos_reply(group_id, todos, search_start_date, target_user)
            
        except Exception as e:
            logger.error(f"[WechatTimer] 提取待办任务失败: {str(e)}")
            return f"提取 {target_user} 的待办任务时出错: {str(e)}"

    def _get_chat_records(self, group_id: str, member: str, start_date: datetime, end_date: datetime) -> List[Dict[str, Any]]:
        """从数据库获取聊天记录"""
        try:
            connection = pymysql.connect(**self.db_config)
            
            with connection.cursor(pymysql.cursors.DictCursor) as cursor:
                # 计算时间范围
                start_time = start_date.strftime('%Y-%m-%d %H:%M:%S')
                end_time = end_date.strftime('%Y-%m-%d %H:%M:%S')
                
                # 查询聊天记录
                sql = """
                SELECT msg_id, msg_type, from_user, room_id, msg_time, content, media_url, extra_info, created_at
                FROM wecom_chat_archive 
                WHERE room_id = %s 
                AND created_at >= %s AND created_at <= %s
                AND msg_type = 'text'
                AND content IS NOT NULL
                AND content != ''
                ORDER BY msg_time ASC
                """
                
                cursor.execute(sql, (group_id, start_time, end_time))
                records = cursor.fetchall()
                
                logger.info(f"[WechatTimer] 查询到 {len(records)} 条聊天记录")
                return records
                
        except Exception as e:
            logger.error(f"[WechatTimer] 查询数据库失败: {str(e)}")
            return []
        finally:
            if 'connection' in locals():
                connection.close()

    def _extract_todos_with_llm(self, chat_records: List[Dict[str, Any]], target_user: str) -> List[Dict[str, Any]]:
        """使用大模型提取指定用户的待办任务
        
        Args:
            chat_records: 聊天记录列表
            target_user: 目标用户名
        
        Returns:
            该用户的待办任务列表
        """
        try:
            # 从配置读取消息数限制
            try:
                config = self.load_config()
                todo_extract_config = config.get('wechat_timer', {}).get('todo_extract_config', {})
                max_messages_per_search = todo_extract_config.get('max_messages_per_search', 100)
            except Exception as e:
                logger.warning(f"[WechatTimer] 读取待办任务提取配置失败，使用默认值: {e}")
                max_messages_per_search = 100
            
            # 准备聊天记录文本
            # 按时间顺序处理聊天记录
            processed_records = sorted(
                chat_records, 
                key=lambda x: datetime.fromtimestamp(x['msg_time'] / 1000)
            )
            
            # 获取当前处理日期范围
            now = datetime.now()
            
            # 聊天记录已经在 _extract_yesterday_todos 中过滤过，这里只需要限制消息数量
            chat_text = ""
            for record in processed_records[-max_messages_per_search:]:  # 限制最近消息数
                timestamp = datetime.fromtimestamp(record['msg_time'] / 1000).strftime('%Y-%m-%d %H:%M')
                chat_text += f"[{timestamp}] {record['from_user']}: {record['content']}\n"
            
            # 计算日期范围用于提示词
            # 找到最早和最晚的记录时间
            if processed_records:
                earliest_record = min(processed_records, key=lambda x: x['msg_time'])
                latest_record = max(processed_records, key=lambda x: x['msg_time'])
                start_date = datetime.fromtimestamp(earliest_record['msg_time'] / 1000).strftime('%Y-%m-%d')
                end_date = datetime.fromtimestamp(latest_record['msg_time'] / 1000).strftime('%Y-%m-%d')
            else:
                # 如果没有记录，使用当前日期
                start_date = now.strftime('%Y-%m-%d')
                end_date = now.strftime('%Y-%m-%d')
            
            # 构建提示词
            prompt = f"""
你是一个极其严谨、注重细节的待办任务提取专家。你的唯一职责是从企业微信聊天记录中，精确地识别并提取需要 "{target_user}" 执行的、明确的未来行动指令。

【处理日期范围】
开始日期: {start_date}
结束日期: {end_date}

【聊天记录】
{chat_text}

【任务提取规则】
1. 仅提取明确指向 "{target_user}" 的待办任务
2. 必须是未来要执行的行动
3. 语义必须明确，可归纳出具体行动

【严格排除规则】
- 过去或已完成的事项
- 纯状态回报或总结
- 模糊无行动的讨论
- 未明确主体的任务

【输出格式】
请严格按照以下JSON格式返回提取的待办任务。如果未发现任何符合定义的待办任务，必须返回空数组[]。
[
    {{
        "task_title": "任务标题（概括性总结）",
        "task_description": "完整的任务详细描述",
        "assignee": "{target_user}",
        "deadline": "截止时间（YYYY-MM-DD HH:MM格式，如果明确指定）",
        "priority": "优先级（高/中/低）",
        "source_msg_id": "来源消息ID",
        "source_user": "发布任务的用户"
    }}
]

【关键判别规则】
- 正向关键词：未来/计划/需要/请/麻烦/安排/负责/跟进/继续/修复/上线/提交/整理/拜访/制作/收集反馈/确认/对齐/开会/发版
- 负向关键词：已完成/完成了/已做/已发/已给到/已修复/今日已完成/今天完成/做完了/通过了测试

【优先级判定】
- 高：今天/明天必须/领导直接指派/客户有时限/到期日明确且临近
- 中：常规工作项
- 低：无紧迫性且无截止时间

【去重规则】
- 语义重复只保留一条
- 合并多处对同一行动的描述
"""
            
            # 调用大模型API
            todos = self._call_llm_api(prompt)
            
            # 过滤并验证任务
            filtered_todos = [
                todo for todo in todos 
                if todo.get('assignee', '').strip() == target_user
            ]
            
            logger.info(f"[WechatTimer] 为 {target_user} 提取到 {len(filtered_todos)} 个待办任务")
            return filtered_todos
            
        except Exception as e:
            logger.error(f"[WechatTimer] 提取待办任务失败: {str(e)}")
            return []

    def _call_llm_api(self, prompt: str) -> List[Dict[str, Any]]:
        """
        调用大模型API
        
        Args:
            prompt: 提示词
            
        Returns:
            提取的任务数据列表
        """
        try:
            headers = {
                "Authorization": f"Bearer {self.llm_api_key}",
                "Content-Type": "application/json"
            }
            
            data = {
                "model": self.llm_model,
                "messages": [
                    {
                        "role": "system",
                        "content": "你是一个专业的待办任务提取助手。请严格按照指定的JSON格式返回结果。"
                    },
                    {
                        "role": "user",
                        "content": prompt
                    }
                ]
            }
            logger.info(f"正在调用大模型API: {self.llm_api_url}")
            logger.info(f"[WechatTimer] 调用大模型API，提示词: {prompt}")
            response = requests.post(self.llm_api_url, headers=headers, json=data, timeout=60)
            response.raise_for_status()
            
            result = response.json()
            logger.info(f"大模型响应: {result}")
            content = result["choices"][0]["message"]["content"]
            
            # 解析JSON结果
            try:
                tasks_data = json.loads(content)
                logger.info(f"成功解析大模型响应，提取到 {len(tasks_data)} 个任务")
                return tasks_data if isinstance(tasks_data, list) else []
            except json.JSONDecodeError as e:
                logger.warning(f"大模型响应不是有效JSON，使用规则提取: {e}")
                return []
            
        except requests.exceptions.RequestException as e:
            logger.error(f"调用大模型API失败: {e}")
            raise e
        except Exception as e:
            logger.error(f"处理大模型响应失败: {e}")
            raise e

    def _parse_llm_response(self, response: str) -> List[Dict[str, Any]]:
        """解析大模型响应"""
        try:
            # 尝试提取JSON部分
            json_match = re.search(r'\[.*\]', response, re.DOTALL)
            if json_match:
                json_str = json_match.group(0)
                todos = json.loads(json_str)
                return todos if isinstance(todos, list) else []
            else:
                # 如果没有找到JSON格式，尝试简单解析
                return self._simple_parse_todos(response)
        except Exception as e:
            logger.error(f"[WechatTimer] 解析大模型响应失败: {str(e)}")
            return self._simple_parse_todos(response)

    def _simple_parse_todos(self, response: str) -> List[Dict[str, Any]]:
        """简单解析待办任务（备用方法）"""
        todos = []
        lines = response.split('\n')
        
        for line in lines:
            line = line.strip()
            if any(keyword in line for keyword in ['待办', '任务', '需要', '完成', '处理', '跟进']):
                todos.append({
                    'task_title': line,
                    'task_description': line,
                    'assignee': '未知',
                    'priority': '中',
                    'deadline': '',
                    'source': line
                })
        
        return todos

    def _format_todos_reply(self, group_id: str, todos: List[Dict[str, Any]], date: datetime, target_user: str) -> str:
        """格式化待办任务回复"""
        date_str = date.strftime('%Y年%m月%d日')
        reply = f"📋 群聊 '{group_id}' {date_str} {target_user} 的待办任务：\n\n"
        
        if not todos:
            reply += f"未发现 {target_user} 的待办任务"
            return reply
        
        for i, todo in enumerate(todos, 1):
            task_title = todo.get('task_title', todo.get('task', '未知任务'))
            reply += f"{i}. {task_title}\n"
            
            if todo.get('task_description') and todo['task_description'] != task_title:
                reply += f"   描述: {todo['task_description']}\n"
            
            if todo.get('assignee') and todo['assignee'] != '未知':
                reply += f"   负责人: {todo['assignee']}\n"
            if todo.get('priority'):
                reply += f"   优先级: {todo['priority']}\n"
            if todo.get('deadline'):
                reply += f"   截止时间: {todo['deadline']}\n"
            reply += "\n"
        
        reply += f"共发现 {len(todos)} 个待办任务"
        return reply

    def _get_access_token(self) -> Optional[str]:
        """获取企业微信访问令牌"""
        try:
            # 检查令牌是否过期
            if self.access_token and datetime.now().timestamp() < self.token_expire_time:
                return self.access_token
            
            # 获取新的访问令牌
            url = f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={self.corp_id}&corpsecret={self.corp_secret}"
            response = requests.get(url)
            result = response.json()
            
            if result.get('errcode') == 0:
                self.access_token = result['access_token']
                self.token_expire_time = datetime.now().timestamp() + result['expires_in'] - 300  # 提前5分钟过期
                return self.access_token
            else:
                logger.error(f"[WechatTimer] 获取访问令牌失败: {result}")
                return None
                
        except Exception as e:
            logger.error(f"[WechatTimer] 获取访问令牌异常: {str(e)}")
            return None

    def _send_replacement_card(self, task: Dict[str, Any], status_text: str, response_code: Optional[str] = None) -> None:
        """
        更新卡片消息，禁用按钮
        
        Args:
            task: 任务信息
            status_text: 状态文本
            response_code: 响应码（可选）
        """
        try:
            # 获取访问令牌
            access_token = self._get_access_token()
            if not access_token:
                logger.error("[WechatTimer] 获取访问令牌失败")
                return
            
            # 检查是否有消息ID和响应码
            if not task.get('message_id'):
                logger.error(f"[WechatTimer] 任务 {task['task_id']} 没有消息ID，无法更新卡片")
                return
            
            if not response_code:
                logger.error(f"[WechatTimer] 任务 {task['task_id']} 没有响应码，无法更新卡片")
                return
            
            # 构造更新卡片请求数据
            update_data = {
                "userids": [task['user_id']],  # 使用userids而不是msgid
                "agentid": self.agent_id,
                "response_code": response_code,
                "template_card": {
                    "card_type": "button_interaction",
                    "source": {
                        "icon_url": "https://wework.qpic.cn/wwpic/252813_jOfDHtcISzuodLa_1629280209/0",
                        "desc": "任务管理",
                        "desc_color": 0
                    },
                    "main_title": {
                        "title": "待办任务通知"
                    },
                    "button_selection": {
                        "question_key": "task_action",
                        "option_list": [
                            {
                                "id": "complete",
                                "text": "完成",
                                "is_disabled": True
                            },
                            {
                                "id": "change_time",
                                "text": "改时间",
                                "is_disabled": True
                            },
                            {
                                "id": "cancel",
                                "text": "不再提醒",
                                "is_disabled": True
                            }
                        ]
                    },
                    "button_list": [
                        {
                            "text": status_text,
                            "style": 1,
                            "key": "submit_key",
                            "disable": True
                        }
                    ],
                    "horizontal_content_list": [
                        {
                            "keyname": "状态",
                            "value": status_text
                        },
                        {
                            "keyname": "内容",
                            "value": task.get('message') or task.get('message_content', "任务已处理")
                        }
                    ]
                }
            }
            
            # 调用更新模板卡片API
            url = f"https://qyapi.weixin.qq.com/cgi-bin/message/update_template_card?access_token={access_token}"
            
            # 记录发送的请求数据
            logger.info(f"[WechatTimer] 更新卡片请求数据: {json.dumps(update_data)}")
            
            # 发送请求
            response = requests.post(url, json=update_data)
            
            # 记录原始响应
            logger.info(f"[WechatTimer] 更新卡片响应状态码: {response.status_code}")
            logger.info(f"[WechatTimer] 更新卡片响应内容: {response.text}")
            
            # 尝试解析响应
            try:
                result = response.json()
                
                # 检查响应结果
                if result.get('errcode') == 0:
                    logger.info(f"[WechatTimer] 更新卡片成功: {task['task_id']}")
                else:
                    logger.error(f"[WechatTimer] 更新卡片失败: {result}")
            
            except json.JSONDecodeError:
                logger.error(f"[WechatTimer] 无法解析响应JSON: {response.text}")
                
        except Exception as e:
            logger.error(f"[WechatTimer] 更新卡片异常: {str(e)}", exc_info=True)
    
    def _log_tasks_status(self) -> None:
        """
        记录当前所有任务的状态（用于调试）
        删除原有的未操作任务检查逻辑
        """
        try:
            now = datetime.now()
            sent_tasks = [t for t in self.timer_tasks if t['status'] == 'sent']
            
            logger.info(f"[WechatTimer] 当前任务状态: 总数={len(self.timer_tasks)}, 已发送={len(sent_tasks)}")
            
        except Exception as e:
            logger.error(f"[WechatTimer] 记录任务状态失败: {str(e)}")
    
    def get_help_text(self, **kwargs) -> str:
        """获取帮助文本"""
        help_text = """
企业微信定时消息插件使用说明：

指令格式：
1. 定时消息：#定时消息 #用户ID #时间 #消息内容
2. 定时待办：#定时待办 #群ID #用户ID

示例：
#定时消息 #zhangsan #2025-10-24 09:00 #早上好，记得开会
#定时消息 #lisi #5分钟后 #提醒：5分钟后开始会议
#定时待办 #wr8gxKDAAAJK07I6WxrwWcMliC9kSHXg #zhangsan

时间格式支持：
- 绝对时间：2025-10-24 09:00:00、2025-10-24 09:00、10-24 09:00、09:00
- 相对时间：5分钟后、1小时后、2天后
- 定时重复：每天09:00、每周一09:00

功能：
- 定时发送企业微信消息给指定用户
- 定时提取前一天群聊中指定用户的待办任务
- 支持多种时间格式
- 智能AI任务提取
- 交互式卡片消息
- 自动管理访问令牌
- 任务状态跟踪

交互式卡片消息特性：
- 支持任务接受/拒绝
- 拒绝后30分钟自动重试
- 可视化任务通知
- 实时交互反馈

待办任务提取特性：
- 精准识别指定用户的待办任务
- 智能过滤和排除无关信息
- 支持自然语言理解
- 严格遵循任务提取规则
- 去重和优先级判断

注意：
- 用户ID需要是企业微信中的用户ID
- 群ID需要是数据库中的room_id
- 定时待办默认每天09:00执行
- 仅提取指定用户的待办任务
- 消息内容支持文本格式
"""
        return help_text